import * as Cesium from '@cesium/Source/Cesium.js';

var EllipseGeometryLibraryEx = (function() {
  var EllipseGeometryLibrary = {};

  var rotAxis = new Cesium.Cartesian3();
  var tempVec = new Cesium.Cartesian3();
  var unitQuat = new Cesium.Quaternion();
  var rotMtx = new Cesium.Matrix3();

  function pointOnEllipsoid(theta, rotation, northVec, eastVec, aSqr, ab, bSqr, mag, unitPos, result) {
    var azimuth = theta + rotation;

    Cesium.Cartesian3.multiplyByScalar(eastVec, Math.cos(azimuth), rotAxis);
    Cesium.Cartesian3.multiplyByScalar(northVec, Math.sin(azimuth), tempVec);
    Cesium.Cartesian3.add(rotAxis, tempVec, rotAxis);

    var cosThetaSquared = Math.cos(theta);
    cosThetaSquared = cosThetaSquared * cosThetaSquared;

    var sinThetaSquared = Math.sin(theta);
    sinThetaSquared = sinThetaSquared * sinThetaSquared;

    var radius = ab / Math.sqrt(bSqr * cosThetaSquared + aSqr * sinThetaSquared);
    var angle = radius / mag;

    // Create the quaternion to rotate the position vector to the boundary of the ellipse.
    Cesium.Quaternion.fromAxisAngle(rotAxis, angle, unitQuat);
    Cesium.Matrix3.fromQuaternion(unitQuat, rotMtx);

    Cesium.Matrix3.multiplyByVector(rotMtx, unitPos, result);
    Cesium.Cartesian3.normalize(result, result);
    Cesium.Cartesian3.multiplyByScalar(result, mag, result);
    return result;
  }

  var scratchCartesian1 = new Cesium.Cartesian3();
  var scratchCartesian2 = new Cesium.Cartesian3();
  var scratchCartesian3 = new Cesium.Cartesian3();
  var scratchNormal = new Cesium.Cartesian3();
  /**
   * Returns the positions raised to the given heights
   * @private
   */
  EllipseGeometryLibrary.raisePositionsToHeight = function(positions, options, extrude) {
    var ellipsoid = options.ellipsoid;
    var height = options.height;
    var extrudedHeight = options.extrudedHeight;
    var size = (extrude) ? positions.length / 3 * 2 : positions.length / 3;

    var finalPositions = new Float64Array(size * 3);

    var length = positions.length;
    var bottomOffset = (extrude) ? length : 0;
    for (var i = 0; i < length; i += 3) {
      var i1 = i + 1;
      var i2 = i + 2;

      var position = Cesium.Cartesian3.fromArray(positions, i, scratchCartesian1);
      ellipsoid.scaleToGeodeticSurface(position, position);

      var extrudedPosition = Cesium.Cartesian3.clone(position, scratchCartesian2);
      var normal = ellipsoid.geodeticSurfaceNormal(position, scratchNormal);
      var scaledNormal = Cesium.Cartesian3.multiplyByScalar(normal, height, scratchCartesian3);
      Cesium.Cartesian3.add(position, scaledNormal, position);

      if (extrude) {
        Cesium.Cartesian3.multiplyByScalar(normal, extrudedHeight, scaledNormal);
        Cesium.Cartesian3.add(extrudedPosition, scaledNormal, extrudedPosition);

        finalPositions[i + bottomOffset] = extrudedPosition.x;
        finalPositions[i1 + bottomOffset] = extrudedPosition.y;
        finalPositions[i2 + bottomOffset] = extrudedPosition.z;
      }

      finalPositions[i] = position.x;
      finalPositions[i1] = position.y;
      finalPositions[i2] = position.z;
    }

    return finalPositions;
  };

  var unitPosScratch = new Cesium.Cartesian3();
  var eastVecScratch = new Cesium.Cartesian3();
  var northVecScratch = new Cesium.Cartesian3();
  /**
   * options.semiMinorAxis：短半轴
   * options.semiMajorAxis：长半轴
   * options.rotation：旋转角度 弧度
   * options.center：中心点 笛卡尔坐标
   * options.granularity：粒度 弧度
     addFillPositions：是否插值
     addEdgePositions：是否添加端点
   * Returns an array of positions that make up the ellipse.
   * @private
   */
  EllipseGeometryLibrary.computeEllipsePositions = function(options, addEdgePositions) {
    var semiMinorAxis = options.semiMinorAxis;
    var semiMajorAxis = options.semiMajorAxis;
    var rotation = options.rotation; //法线
    var center = options.center;
    var granularity = options.granularity && (typeof options.granularity === "number") ? options.granularity : (
      Math.PI / 180.0); // 角度间隔
    if (granularity > Math.PI / 12.0) {
      granularity = Math.PI / 12.0;
    }
    if (granularity < Math.PI / 180.0) {
      granularity = Math.PI / 180.0;
    }
    var aSqr = semiMinorAxis * semiMinorAxis;
    var bSqr = semiMajorAxis * semiMajorAxis;
    var ab = semiMajorAxis * semiMinorAxis;

    var mag = Cesium.Cartesian3.magnitude(center); //模

    var unitPos = Cesium.Cartesian3.normalize(center, unitPosScratch);
    var eastVec = Cesium.Cartesian3.cross(Cesium.Cartesian3.UNIT_Z, center, eastVecScratch);
    eastVec = Cesium.Cartesian3.normalize(eastVec, eastVec);
    var northVec = Cesium.Cartesian3.cross(unitPos, eastVec, northVecScratch);
    var numPts = 1 + Math.ceil(Cesium.Math.PI_OVER_TWO / granularity);

    var deltaTheta = Cesium.Math.PI_OVER_TWO / (numPts - 1);
    var theta = Cesium.Math.PI_OVER_TWO - numPts * deltaTheta;
    if (theta < 0.0) {
      numPts -= Math.ceil(Math.abs(theta) / deltaTheta);
    }
    var positions = new Array((numPts + 1) * 3);
    var positionsdown = new Array((numPts + 1) * 3);

    var positionIndex = 0;
    var positionsdownIndex = 0;
    var position = scratchCartesian1;
    var reflectedPosition = scratchCartesian2;

    var outerPositionsLength = (numPts * 4) * 3;
    var outerRightIndex = outerPositionsLength - 1;
    var outerLeftIndex = 0;
    var outerPositions = (addEdgePositions) ? new Array(outerPositionsLength) : undefined;

    var i;
    var j;
    var numInterior;
    var t;
    var interiorPosition;

    theta = Cesium.Math.PI_OVER_TWO;
    position = pointOnEllipsoid(theta, rotation, northVec, eastVec, aSqr, ab, bSqr, mag, unitPos, position);
    positions[positionIndex++] = position.x;
    positions[positionIndex++] = position.y;
    positions[positionIndex++] = position.z;
    if (addEdgePositions) {
      outerPositions[outerRightIndex--] = position.z;
      outerPositions[outerRightIndex--] = position.y;
      outerPositions[outerRightIndex--] = position.x;
    }

    theta = Cesium.Math.PI_OVER_TWO - deltaTheta;
    for (i = 1; i < numPts + 1; ++i) {
      position = pointOnEllipsoid(theta, rotation, northVec, eastVec, aSqr, ab, bSqr, mag, unitPos, position);
      reflectedPosition = pointOnEllipsoid(Math.PI - theta, rotation, northVec, eastVec, aSqr, ab, bSqr, mag,
        unitPos, reflectedPosition);
      positionsdown[positionsdownIndex++] = position.x;
      positionsdown[positionsdownIndex++] = position.y;
      positionsdown[positionsdownIndex++] = position.z;
      positions[positionIndex++] = reflectedPosition.x;
      positions[positionIndex++] = reflectedPosition.y;
      positions[positionIndex++] = reflectedPosition.z;
      if (addEdgePositions) {
        outerPositions[outerRightIndex--] = position.z;
        outerPositions[outerRightIndex--] = position.y;
        outerPositions[outerRightIndex--] = position.x;
        outerPositions[outerLeftIndex++] = reflectedPosition.x;
        outerPositions[outerLeftIndex++] = reflectedPosition.y;
        outerPositions[outerLeftIndex++] = reflectedPosition.z;
      }

      theta = Cesium.Math.PI_OVER_TWO - (i + 1) * deltaTheta;
    }
    for (i = numPts; i > 1; --i) {
      theta = Cesium.Math.PI_OVER_TWO - (i - 1) * deltaTheta;

      position = pointOnEllipsoid(-theta, rotation, northVec, eastVec, aSqr, ab, bSqr, mag, unitPos, position);
      reflectedPosition = pointOnEllipsoid(theta + Math.PI, rotation, northVec, eastVec, aSqr, ab, bSqr, mag,
        unitPos, reflectedPosition);
      positionsdown[positionsdownIndex++] = position.x;
      positionsdown[positionsdownIndex++] = position.y;
      positionsdown[positionsdownIndex++] = position.z;
      positions[positionIndex++] = reflectedPosition.x;
      positions[positionIndex++] = reflectedPosition.y;
      positions[positionIndex++] = reflectedPosition.z;
      if (addEdgePositions) {
        outerPositions[outerRightIndex--] = position.z;
        outerPositions[outerRightIndex--] = position.y;
        outerPositions[outerRightIndex--] = position.x;
        outerPositions[outerLeftIndex++] = reflectedPosition.x;
        outerPositions[outerLeftIndex++] = reflectedPosition.y;
        outerPositions[outerLeftIndex++] = reflectedPosition.z;
      }
    }

    theta = Cesium.Math.PI_OVER_TWO;
    position = pointOnEllipsoid(-theta, rotation, northVec, eastVec, aSqr, ab, bSqr, mag, unitPos, position);

    var r = {};
    positionsdown[positionsdownIndex++] = position.x;
    positionsdown[positionsdownIndex++] = position.y;
    positionsdown[positionsdownIndex++] = position.z;

    r.positions = positions;
    r.positionsdown = positionsdown;
    r.numPts = numPts;
    if (addEdgePositions) {
      outerPositions[outerRightIndex--] = position.z;
      outerPositions[outerRightIndex--] = position.y;
      outerPositions[outerRightIndex--] = position.x;
      r.outerPositions = outerPositions;
    }
    return r;
  };

  /**
   * options.semiMinorAxis：短半轴
   * options.semiMajorAxis：长半轴
   * options.rotation：旋转角度 弧度
   * options.center：中心点 笛卡尔坐标
   * options.granularity：粒度 弧度
   * Returns an array of positions that make up the ellipse.
   * @private
   */
  EllipseGeometryLibrary.computeEllipseEdgePositions = function(options) {
    var semiMinorAxis = options.semiMinorAxis;
    var semiMajorAxis = options.semiMajorAxis;
    var rotation = options.rotation; //法线
    var center = options.center;
    var granularity = options.granularity && (typeof options.granularity === "number") ? options.granularity : (
      Math.PI / 180.0); // 角度间隔
    if (granularity > Math.PI / 12.0) {
      granularity = Math.PI / 12.0;
    }
    if (granularity < Math.PI / 180.0) {
      granularity = Math.PI / 180.0;
    }
    var aSqr = semiMinorAxis * semiMinorAxis;
    var bSqr = semiMajorAxis * semiMajorAxis;
    var ab = semiMajorAxis * semiMinorAxis;
    var mag = Cesium.Cartesian3.magnitude(center); //
    var unitPos = Cesium.Cartesian3.normalize(center, unitPosScratch);
    var eastVec = Cesium.Cartesian3.cross(Cesium.Cartesian3.UNIT_Z, center, eastVecScratch);
    eastVec = Cesium.Cartesian3.normalize(eastVec, eastVec);
    var northVec = Cesium.Cartesian3.cross(unitPos, eastVec, northVecScratch);
    var numPts = Math.ceil(Cesium.Math.PI * 2 / granularity);
    var deltaTheta = granularity;
    var theta = 0;

    var position = scratchCartesian1;
    var i;
    var outerIndex = 0;
    var outerPositions = [];
    for (i = 0; i < numPts; i++) {
      theta = i * deltaTheta;
      position = pointOnEllipsoid(theta, rotation, northVec, eastVec, aSqr, ab, bSqr, mag, unitPos, position);

      outerPositions[outerIndex++] = position.x;
      outerPositions[outerIndex++] = position.y;
      outerPositions[outerIndex++] = position.z;
    }

    var r = {};
    r.numPts = numPts;
    r.outerPositions = outerPositions;
    return r;
  };
  return EllipseGeometryLibrary;
})();

export default EllipseGeometryLibraryEx;
